2022-08-23
Wardialing My Grandmother
Before we get to the technical things, I think I should first explain the little ritual my grandmother and I have developed over the years. I call her a few times a month and we usually talk for about an hour. However, neither of us likes setting a date for this, so our strategy is that I just call her when I have time. I can't call her mobile phone, because it is usually in silent mode. So instead I have to call her on the landline. Usually she does not pick up right away. What works best is ringing four or five times, hanging up and just waiting for her to reach the phone and call me back.
Now, I use a pre-paid SIM card, because I don't make enough calls for a contract to be worth it and because I also don't need mobile internet (every place I need to be has WiFi these days). Recently I ran out of money on the card, so could not call using my mobile phone.
Instead I had a fun idea: I have an old rotary-dial phone sitting on a shelf behind me and the router/phone-system hybrid boxes that are common here in Germany still support them, in theory. So I fetched the phone and plugged it in. It did not work. I am pretty sure the phone is functioning perfectly, so probably an issue with the box.
Then I remembered that these things allow you to dial the phone via the webinterface (the way old phones worked allows for the dialing device and the device used for the actual call to be different). It was a bit clunky, but worked. My grandmother calls me first when she does not recognize a number that called her, for legacy reasons. So me using my previously never touched landline connection was not a problem. We had a nice talk that day, as usual.
About a week later I thought that I should be able to automate this.
At first I tried to just mimic how user input would be over the webinterface with curl, using a helpful write-up. Unfortunately, I did not get past the initial login, likely because things have changed since that article was written. I am not proficient at reverse-engineering web-things, so this first attempt died here.
Then I found out that there was apparently a control interface / protocol (?) that can be used locally to control the box, called TR064. And there is a python package for it! Unfortunately I could figure out how to make that package emit the right web-call-thingy for dialing to work. Dialing is apparently a custom add-on by the manufacturer and not in the standard.
I did find a reference, inluding also the custom extensions. It did not help me with the python library, so I tried searching online for some of the interface names. When searching for "X_AVM-DE_DialNumber", I got lucky. Why can't things always be this nice?
I found an article that has an example of using the TR064 thing with curl and netcat! Using netcat apparently doesn't work anymore, likely because a lot of the interfaces got deprecated. But the curl example still works! Not very reliable, one out of three attempts fails.
Oh, and now apparently all attempts fail? What? Maybe I called myself a bit too often and some system somewhere decided it needs to block the number now? No, wait, I simply did not have reception at my desk. That is technically a bit weirder, but let's move on.
Anyway, when it fails to order an auto-dial, we actually get a response, telling us that it fails! It doesn't tell us why it fails, at least not to my knowledge, but who needs such information anyway, right? So my outrageously horrible script checks if we got the dreaded error response. If yes, try again. If no ...
To understand what needs to happen next, we need to understand how the auto-dialer is supposed to function. When you auto-dial a number, the box calls the number. If the line is picked up, the box then calls your phone locally. A bit rude if you ask me. Luckily it also works without a phone connected; I prefer my rotary phone on my shelf.
So what I want the box to do after dialing is hang up. The dialing process takes some time, so I experimented a bit until I found a delay between the dial and hangup orders that worked for my purpose.
#!/bin/bash
shopt -s lastpipe
USER="fritz0944"
PASS="hunter2"
DIAL="0123456789"
function dial
{
curl -4 -k --anyauth -u "${USER}:${PASS}" \
"http://fritz.box:49000/upnp/control/x_voip" \
-H 'Content-Type: text/xml; charset="utf-8"' \
-H 'SoapAction: urn:dslforum-org:service:X_VoIP:1#X_AVM-DE_DialNumber' \
-d '<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:X_AVM-DE_DialNumber xmlns:u="urn:dslforum-org:service:X_VoIP:1">
<NewX_AVM-DE_PhoneNumber>'${DIAL}'</NewX_AVM-DE_PhoneNumber>
</u:X_AVM-DE_DialNumber>
</s:Body>
</s:Envelope>'
}
function hangup
{
curl -4 -k --anyauth -u "${USER}:${PASS}" \
"http://fritz.box:49000/upnp/control/x_voip" \
-H 'Content-Type: text/xml; charset="utf-8"' \
-H 'SoapAction: urn:dslforum-org:service:X_VoIP:1#X_AVM-DE_DialHangup' \
-d '<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:X_AVM-DE_DialHangup xmlns:u="urn:dslforum-org:service:X_VoIP:1">
</u:X_AVM-DE_DialHangup>
</s:Body>
</s:Envelope>'
}
echo "Dialing..."
while true
do
dial | read RESPONSE
[ -z "$(echo "${RESPONSE}" | grep -i error)" ] && break
sleep 1
echo "(again)"
done
sleep 13
echo "Hanging up..."
while true
do
hangup | read RESPONSE
[ -z "$(echo "${RESPONSE}" | grep -i error)" ] && break
sleep 1
echo "(again)"
done
So now I have a script I can execute whenever I want to call my grandmother, which automates the first few step of our usual calling ritual.
While this was certainly a fun hack, the real value here is learning of TR064 and how to curl it, opening up an entire world of stupid very sane and mentally healthy things to do.